nhn.Component = jindo.$Class({

	_eventHandlers : null,
	_options : null,

	$init  : function() {
		var ins = this.constructor._instances;
		if (typeof ins == "undefined") {
			this.constructor._instances = ins = [];
		}
		ins[ins.length] = this;
		this._eventHandlers = {};
		this._options = {};
		this._options._setters = {};
	},
	
	/**
	 * 옵션값을 설정하거나 가져온다. 
	 * @param {String} sName 옵션의 이름
	 * @param {String} sValue 옵션의 값
	 */
	option : function(sName, sValue) {
		var nameType = (typeof sName);

		if (nameType == "undefined") {
			return this._options;
		} else if (nameType == "string") {
			if (typeof sValue != "undefined") {
				this._options[sName] = sValue;
				if (typeof this._options._setters[sName] == "function") {
					this._options._setters[sName](sValue);	
				}
				return this;
			} else {
				return this._options[sName];
			}
		} else if (nameType == "object") {
			try {
				for(var x in sName) {
					this._options[x] = sName[x];
					if (typeof this._options._setters[x] == "function") {
						this._options._setters[x](sName[x]);	
					}
				}
			} catch(e) {}
			return this;
		}
	},
	
	/**
	 * 옵션의 setter 함수를 설정하거나 가져온다. 
	 * @param {String} sName setter의 이름
	 * @param {Function} fSetter setter 함수
	 */
	optionSetter : function(sName, fSetter) {
		var nameType = (typeof sName);

		if (nameType == "undefined") {
			return this._options._setters;
		} else if (nameType == "string") {
			if (typeof fSetter != "undefined") {
				this._options._setters[sName] = jindo.$Fn(fSetter, this).bind();
				return this;
			} else {
				return this._options._setters[sName];
			}
		} else if (nameType == "object") {
			try {
				for(var x in sName) {
					this._options._setters[x] = jindo.$Fn(sName[x], this).bind();
				}
			} catch(e) {}
			return this;
		}		
	},
	
	/**
	 * 커스텀 이벤트를 발생시킨다.
	 * @param {Object} sEvent
	 * @param {Object} oEvent
	 */
	fireEvent : function(sEvent, oEvent) {
	
		var oEvent = oEvent ? (oEvent instanceof jindo.$Event ? oEvent._event : oEvent) : {};
		
		var inlineHandler = this['on' + sEvent];
		var handlerList = this._eventHandlers[sEvent];
		
		var bHasInlineHandler = typeof inlineHandler == 'function';
		var bHasHandlerList = typeof handlerList != 'undefined';
	
		if (!bHasInlineHandler && !bHasHandlerList) return true;
		
		var isRealEvent = (function(oEvent) {
									
			try { if (oEvent instanceof Event) return true; } catch(x) { }
			// try { if (oEvent instanceof HTMLEvent) return true; } catch(x) { } // 이걸 넣으면 FF 에서 속도가 너무 느려짐
			try { if (oEvent instanceof MouseEvent) return true; } catch(x) { }
			try { if (oEvent instanceof KeyEvent) return true; } catch(x) { }
			try { if (('cancelBubble' in oEvent || 'preventBubble' in oEvent) && 'type' in oEvent) return true; } catch(x) { }
			
			return false;
			
		})(oEvent);
		
		if (!isRealEvent) {
			
			try {

				if (typeof oEvent._extends == 'undefined') {
					
					oEvent._extends = [];
					oEvent.stop = function() { 
						this._extends[this._extends.length - 1].canceled = true;
					};
					
				}
				
				oEvent._extends.push({
					type : sEvent,
					canceled : false
				});
				
				oEvent.type = sEvent;
				
			} catch(e) {
				isRealEvent = true;
			}
			
		}
		
		if (isRealEvent) oEvent = jindo.$Event(oEvent);
	
		var aArg = [ oEvent ];
	
		for (var i = 2, len = arguments.length; i < len; i++)
			aArg.push(arguments[i]);
		
		if (bHasInlineHandler) inlineHandler.apply(this, aArg);
	
		if (bHasHandlerList)
			for (var i = 0, handler; handler = handlerList[i]; i++)
				handler.apply(this, aArg);

		if (isRealEvent) return !oEvent.canceled;
		
		var oPopedEvent = oEvent._extends.pop();
		return !oPopedEvent.canceled;
	
	},

	/**
	 * 커스텀 이벤트 핸들러를 등록한다.
	 * @param {Object} sEvent
	 * @param {Object} fHandler
	 */
	attach : function(sEvent, fHandler) {

		if (arguments.length == 1) {

			jindo.$H(arguments[0]).forEach(jindo.$Fn(function(fHandler, sEvent) {
				this.attach(sEvent, fHandler);
			}, this).bind());
		
			return this;
		}

		var handlers = this._eventHandlers[sEvent];

		if (typeof handlers == 'undefined')
			handlers = this._eventHandlers[sEvent] = [];

		handlers.push(fHandler);

		return this;

	},
	
	/**
	 * 커스텀 이벤트 핸들러를 해제한다.
	 * @param {Object} sEvent
	 * @param {Object} fHandler
	 */
	detach : function(sEvent, fHandler) {

		if (arguments.length == 1) {

			jindo.$H(arguments[0]).forEach(jindo.$Fn(function(fHandler, sEvent) {
				this.detach(sEvent, fHandler);
			}, this).bind());
		
			return this;
		}

		var handlers = this._eventHandlers[sEvent];

		if (typeof handlers == 'undefined') return this;

		for (var i = 0, handler; handler = handlers[i]; i++) {
			if (handler === fHandler) {
				handlers = handlers.splice(i, 1);
				break;
			}
		}

		return this;

	},
	
	/**
	 * 등록된 모든 커스텀 이벤트 핸들러를 해제한다.
	 * @param {Object} sEvent
	 */
	detachAll : function(sEvent) {
		var handlers = this._eventHandlers;
		
		if (arguments.length) {
			
			if (typeof handlers[sEvent] == 'undefined') return this;
	
			delete handlers[sEvent];
	
			return this;
		}	
		
		for (var o in handlers) {
			delete handlers[o];
		}
		return this;				
	}

});